//-------------------------------------------------------------------------------------------------
// Copyright (c) Bradford W. Mott and Flare Contributors
// North Carolina State University, Department of Computer Science
// The IntelliMedia Group
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Ionic.Zip;

namespace BuildPackage
{
    /// <summary>
    /// This is a simple program for building a release of Flare. When executed it compiles
    /// the Flare assemblies and builds a Unity package for the release. It also creates a
    /// zip file of the sources used to create the assemblies.
    /// 
    /// NOTE: Before executing the program update the version information as necessary and 
    /// ensure that the folder /Users/Shared/Flare exists since the project is copied to this
    /// folder for building the assemblies and package.
    /// </summary>
	class MainClass
	{
        // Version format is "major.minor.build.revision-type" where build is the number
        // of days since 2014/01/01, revision is the number of minutes since midnight,
        // and type is "Alpha", "Beta", or "Release"
        public static int major = 1;
        public static int minor = 0;
        public static bool alpha = true;
        public static bool beta = false;

		public static void Main(string[] args)
		{
            string compiler;
            string unity;
            string unityLibPath;

            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                unity = "\"C:/Program Files (x86)/Unity/Editor/Unity.exe\"";
                compiler = "\"C:/Program Files (x86)/Unity/Editor/Data/Mono/bin/gmcs.bat\"";
                unityLibPath = "\"C:/Program Files (x86)/Unity/Editor/Data/Managed\"";
            }
            else
            {
                unity = "/Applications/Unity/Unity.app/Contents/MacOS/Unity";
                compiler = "/Applications/Unity/Unity.app/Contents/Frameworks/Mono/bin/gmcs";
                unityLibPath = "/Applications/Unity/Unity.app/Contents/Frameworks/Managed/";
            }

            string version = GetVersionString();
            Console.WriteLine("Building Version: " + version);

			string currentDir = Environment.CurrentDirectory;
			string projectPath = System.IO.Path.GetFullPath(currentDir + "/../../../../../");

            // --- Step 0: Copy Assembly Source to Build Directory ---
            string buildPath = string.Format(
                (Environment.OSVersion.Platform == PlatformID.Win32NT) ?
                "C:/Users/Shared/Flare/{0}/" : "/Users/Shared/Flare/{0}/", version);
            Console.WriteLine("\nCopying files to " + buildPath);
            CopyDirectory(projectPath + "Assets", buildPath + "Assets", true);
            CopyDirectory(projectPath + "ProjectSettings", buildPath + "ProjectSettings", true);

            // --- Step 1: Generate AssemblyInfo files ---
            Console.WriteLine("\nCreating AssemblyInfo files");

            System.IO.File.Copy(projectPath + "Source/BuildPackage/meta/AssemblyInfo.cs",
                buildPath + "Assets/Flare/Source/Flare/AssemblyInfo.cs", true);
            using (StreamWriter sw = File.AppendText(
                buildPath + "Assets/Flare/Source/Flare/AssemblyInfo.cs"))
            {
                sw.WriteLine("[assembly: AssemblyTitle(\"Flare Runtime\")]");
                sw.WriteLine(string.Format("[assembly: AssemblyVersion(\"{0}.{1}.0.0\")]",
                    major, minor));
                sw.WriteLine(string.Format("[assembly: AssemblyFileVersion(\"{0}\")]",
                    version.Split('-')[0]));
            }

            System.IO.File.Copy(projectPath + "Source/BuildPackage/meta/AssemblyInfo.cs",
                buildPath + "Assets/Flare/Source/Editor/AssemblyInfo.cs", true);
            using (StreamWriter sw = File.AppendText(
                buildPath + "Assets/Flare/Source/Editor/AssemblyInfo.cs"))
            {
                sw.WriteLine("[assembly: AssemblyTitle(\"Flare Editor\")]");
                sw.WriteLine(string.Format("[assembly: AssemblyVersion(\"{0}.{1}.0.0\")]",
                                           major, minor));
                sw.WriteLine(string.Format("[assembly: AssemblyFileVersion(\"{0}\")]",
                    version.Split('-')[0]));
            }

			// --- Step 2: Build assemblies ---

			// Build the Runtime assembly
			Console.WriteLine("\nBuilding runtime assembly");
			Process p = new Process();
			p.StartInfo.WorkingDirectory = buildPath + "Assets";
			p.StartInfo.FileName = compiler;
			p.StartInfo.Arguments =
                " -target:library -out:Flare/Core/Plugins.NET/FlareRuntime.dll" + 
				" -lib:Flare/Core/Plugins.NET/,Flare/Core/Plugins.NET/ThirdParty," + unityLibPath +
				" -debug -d:RUNTIME" +
				" -r:UnityEngine.dll,Ionic.Zlib.dll,Poly2Tri.dll" +
                " -recurse:Flare/Source/Flare/*.cs";
			p.Start();
			p.WaitForExit();

			// Build the Editor assembly
			Console.WriteLine("\nBuilding editor assembly");
			p = new Process();
			p.StartInfo.WorkingDirectory = buildPath + "Assets";
			p.StartInfo.FileName = compiler;
			p.StartInfo.Arguments =
                " -target:library -out:Flare/Core/Plugins.NET/Editor/FlareEditor.dll" +
				" -lib:Flare/Core/Plugins.NET/,Flare/Core/Plugins.NET/ThirdParty," + unityLibPath +
				" -debug -d:EDITOR" +
				" -r:FlareRuntime.dll" +
				" -r:UnityEngine.dll,UnityEditor.dll,Ionic.Zlib.dll,Poly2Tri.dll" +
                " -recurse:Flare/Source/Editor/*.cs";
			p.Start();
			p.WaitForExit();

			// --- Step 3: Copy Unity meta files for assemblies ---

			// Copy the pre-generated meta files into the Unity Asset folder
			Console.WriteLine("\nCopying meta files for assemblies");
			System.IO.File.Copy(projectPath + "Source/BuildPackage/meta/FlareRuntime.dll.meta",
				buildPath + "Assets/Flare/Core/Plugins.NET/FlareRuntime.dll.meta", true);

			System.IO.File.Copy(projectPath + "Source/BuildPackage/meta/FlareEditor.dll.meta",
				buildPath + "Assets/Flare/Core/Plugins.NET/Editor/FlareEditor.dll.meta", true);

			// --- Step 4: Build Unity Package  ---

			// Move the Flare source out of the Unity project so it does conflict with assemblies
			System.IO.Directory.Move(buildPath + "Assets/Flare/Source",
                currentDir + "/Source");
			System.IO.Directory.Move(buildPath + "Assets/Flare/Source.meta",
                currentDir + "/Source.meta");
		
			// Build Unity package for the project
			Console.WriteLine("\nBuilding Unity package...");
			p = new Process();
			p.StartInfo.WorkingDirectory = buildPath;
			p.StartInfo.FileName = unity;
			p.StartInfo.Arguments =
                "-batchmode -logFile UnityEditorLog.txt -projectPath " + buildPath +
				" -exportPackage Assets/Flare/Core Assets/Flare/Examples Assets/Gizmos " + 
				"Flare-" + version + ".unitypackage" +
				" -quit";
			p.Start();
			p.WaitForExit();
			if (p.ExitCode != 0)
			{
				Console.WriteLine("\n*** Building Package Failed! See UnityEditorLog.txt!");
			}

			// Move the Source back to the Unity project
			System.IO.Directory.Move(currentDir + "/Source",
                buildPath + "Assets/Flare/Source");
			System.IO.Directory.Move(currentDir + "/Source.meta",
                buildPath + "Assets/Flare/Source.meta");

            // -- Step 5: Build zip archive of sources ---
            Console.WriteLine("\nCreating source zip archive...");
            CreateSourceZip(buildPath, version);

			// -- Step 6: Remove temp files ---

			Console.WriteLine("\nCleaning up...");
            System.IO.Directory.Delete(buildPath + "Library", true);
		}
         
        private static string GetVersionString()
        {
            DateTime now = DateTime.Now;
            TimeSpan ts = now - (new DateTime(2014,1,1));
            
            int days = ts.Days;
            int minutes = now.Hour * 60 + now.Minute;
            string type = alpha ? "Alpha" : (beta ? "Beta" : "Release");
            
            return string.Format("{0}.{1}.{2:0000}.{3:0000}-{4}",
                                 major, minor, days, minutes, type);
        }

        private static void CreateSourceZip(string buildPath, string version)
        {
            // Create list of C# source files for the Flare Runtime and Editor
            List<string> files = SourceFiles(buildPath + "Assets/Flare/Source");

            // Build a zip file containing all of the Flare source
            using (ZipFile zip = new ZipFile())
            {
                foreach(string file in files)
                {
                    FileInfo fi = new FileInfo(file);
                    string path = fi.DirectoryName.Substring(fi.DirectoryName.IndexOf(version));

                    zip.AddFile(file, path);
                }

                zip.Save(buildPath + "Flare-Source-" + version + ".zip");
            }
        }

        private static List<string> SourceFiles(string path)
        {
            // Get the subdirectories for the specified directory.
            DirectoryInfo dir = new DirectoryInfo(path);
            DirectoryInfo[] dirs = dir.GetDirectories();
            
            if (!dir.Exists)
            {
                throw new DirectoryNotFoundException(
                    "Directory does not exist or could not be found: " + path);
            }

            List<string> fileList = new List<string>();

            // Get all of the C# files in this directory
            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string fullName = file.FullName;
                if (fullName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
                {
                    fileList.Add(file.FullName);
                }
            }
             
            foreach (DirectoryInfo subdir in dirs)
            {
                List<string> list = SourceFiles(subdir.FullName);
                foreach(string name in list)
                {
                    fileList.Add(name);
                }
            }

            return fileList;
        }

        private static void CopyDirectory(string sourceDirName, string destDirName,
            bool copySubDirs)
        {
            // Get the subdirectories for the specified directory.
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);
            DirectoryInfo[] dirs = dir.GetDirectories();
            
            if (!dir.Exists)
            {
                throw new DirectoryNotFoundException(
                    "Source directory does not exist or could not be found: "
                    + sourceDirName);
            }
            
            // If the destination directory doesn't exist, create it. 
            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }
            
            // Get the files in the directory and copy them to the new location.
            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temppath = Path.Combine(destDirName, file.Name);
                file.CopyTo(temppath, false);
            }
            
            // If copying subdirectories, copy them and their contents to new location. 
            if (copySubDirs)
            {
                foreach (DirectoryInfo subdir in dirs)
                {
                    string temppath = Path.Combine(destDirName, subdir.Name);
                    CopyDirectory(subdir.FullName, temppath, copySubDirs);
                }
            }
        }
	}
}
